package com.acme.bankapp.domain.account;

import com.acme.bankapp.exceptions.OverDraftLimitExceededException;

public class CheckingAccount extends AbstractAccount {

	private static final String ERROR_OVERDRAFT = "Cannot set overdraft smaller than 0!";

	private final double overdraft;
	private double currentOverdraft;

	/**
	 * Creates checking account
	 *
	 * @param id
	 * @param amount
	 * @param overdraft
	 * @throws IllegalArgumentException
	 *             if balance is negative
	 */
	public CheckingAccount(final int id, final double amount,
			final double overdraft) {
		super(id, amount);
		if (overdraft < 0) {
			throw new IllegalArgumentException(ERROR_OVERDRAFT);
		}
		this.overdraft = overdraft;
		this.currentOverdraft = overdraft;
	}

	@Override
	public double maximumAmountToWithdraw() {
		return super.maximumAmountToWithdraw() + currentOverdraft;
	}

	@Override
	public void withdraw(double amount) throws OverDraftLimitExceededException {
		if (amount < 0) {
			throw new IllegalArgumentException(ERROR_AMOUNT_NEGATIVE);
		}

		if (amount <= balance) {
			balance -= amount;
			assert (balance >= 0);
			return;
		}

		if (amount <= (balance + currentOverdraft)) {
			amount -= balance;
			balance = 0;
			currentOverdraft -= amount;
			assert (balance >= 0);
			assert (currentOverdraft >= 0);
			return;
		}

		throw new OverDraftLimitExceededException(amount, this);
	}

	@Override
	public void deposit(double amount) throws IllegalArgumentException {
		if (amount < 0) {
			throw new IllegalArgumentException(ERROR_AMOUNT_NEGATIVE);
		}

		double delta = overdraft - currentOverdraft;
		if (amount <= delta) {
			currentOverdraft += amount;
		} else {
			amount -= delta;
			currentOverdraft = overdraft;
			balance += amount;
		}
	}

	@Override
	public int hashCode() {
		long curOverdraftBits = Double.doubleToLongBits(currentOverdraft);
		long overdraftBits = Double.doubleToLongBits(overdraft);
		int result = 17;
		result = 37 * result
				+ (int) (curOverdraftBits ^ (curOverdraftBits >>> 32));
		result = 37 * result + (int) (overdraftBits ^ (overdraftBits >>> 32));
		result = 37 * result + super.hashCode();
		return result;
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (!(o instanceof CheckingAccount)) {
			return false;
		}

		final CheckingAccount ca = (CheckingAccount) o;
		if ((id != ca.id)) {
			return false;
		}
		if (Double.doubleToLongBits(balance) != Double
				.doubleToLongBits(ca.balance)) {
			return false;
		}
		if (Double.doubleToLongBits(overdraft) != Double
				.doubleToLongBits(ca.overdraft)) {
			return false;
		}
		if (Double.doubleToLongBits(currentOverdraft) != Double
				.doubleToLongBits(ca.currentOverdraft)) {
			return false;
		}
		return true;
	}

	@Override
	public String toString() {
		return super.toString() + " [overdraft: " + currentOverdraft + "]";
	}
}
